/**
 * 操作数栈
 * User: Zhikang.Peng
 * Date: 2018/03/27 11:07
 */
package rtda

import (
	"gitee.com/thghh/jvmgo/rtda/heap"
	"math"
)

/**
 * size 栈大小,因为用数组实现，所以size指向栈顶位置
 */
type OperandStack struct {
	size  uint
	slots []Slot
}

func newOperandStack(maxStack uint) *OperandStack {
	if maxStack > 0 {
		return &OperandStack{
			size:  0,
			slots: make([]Slot, maxStack),
		}
	}
	return nil
}

func (self *OperandStack) PushInt(value int32) {
	self.slots[self.size].num = value
	self.size++
}

func (self *OperandStack) PopInt() int32 {
	self.size--
	return self.slots[self.size].num
}

func (self *OperandStack) PushFloat(value float32) {
	bits := math.Float32bits(value)
	self.slots[self.size].num = int32(bits)
	self.size++
}

func (self *OperandStack) PopFloat() float32 {
	self.size--
	bits := uint32(self.slots[self.size].num)
	return math.Float32frombits(bits)
}

// long consumes two slots
func (self *OperandStack) PushLong(value int64) {
	self.slots[self.size].num = int32(value)         // 低位
	self.slots[self.size+1].num = int32(value >> 32) // 高位
	self.size += 2
}

func (self *OperandStack) PopLong() int64 {
	self.size -= 2
	low := uint32(self.slots[self.size].num)
	high := uint32(self.slots[self.size+1].num)
	return int64(high)<<32 | int64(low)
}

// double consumes two slots
func (self *OperandStack) PushDouble(value float64) {
	bits := math.Float64bits(value)
	self.PushLong(int64(bits))
}

func (self *OperandStack) PopDouble() float64 {
	bits := uint64(self.PopLong())
	return math.Float64frombits(bits)
}

func (self *OperandStack) PushReference(ref *heap.Object) {
	self.slots[self.size].ref = ref
	self.size++
}

func (self *OperandStack) PopReference() *heap.Object {
	self.size--
	ref := self.slots[self.size].ref
	self.slots[self.size].ref = nil // 去除引用，go垃圾回收
	return ref
}
func (self *OperandStack) PushSlot(slot Slot) {
	self.slots[self.size] = slot
	self.size++
}

func (self *OperandStack) PopSlot() Slot {
	self.size--
	return self.slots[self.size]
}

// 返回距离操作数栈顶n个单元格的引用变量
func (self *OperandStack) GetRefFromTop(index uint) *heap.Object {
	// 因为size比实际数组大小大一，所以减去一
	return self.slots[self.size-1-index].ref
}

func (self *OperandStack) PushBoolean(val bool) {
	if val {
		self.PushInt(1)
	} else {
		self.PushInt(0)
	}
}

func (self *OperandStack) PopBoolean() bool {
	return self.PopInt() == 1
}

func (self *OperandStack) Clear() {
	self.size = 0
	for i := range self.slots {
		self.slots[i].ref = nil
	}
}

func NewOperandStack(maxStack uint) *OperandStack {
	return newOperandStack(maxStack)
}
